package com.sunday.common.http.apache.async;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.sunday.common.core.constants.LogConstants;
import com.sunday.common.core.constants.StringConstants;
import com.sunday.common.core.enums.HttpExpandStatusEnum;
import com.sunday.common.http.apache.HttpResult;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.HttpStatus;

import java.nio.charset.StandardCharsets;

/**
 * 实现了Apache HttpClient异步HTTP客户端库中的FutureCallback<SimpleHttpResponse>接口，主要用于处理异步HTTP请求的回调结果。
 *
 * @author zsy
 * @since 2024/1/18
 */
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
public class DefaultFutureCallback implements FutureCallback<SimpleHttpResponse> {

    private MethodCallback callback;

    private String encoding = StandardCharsets.UTF_8.toString();

    public DefaultFutureCallback(MethodCallback callback) {
        this.callback = callback;
    }

    @Override
    public void completed(SimpleHttpResponse response) {
        executeCallback(universalTranslator(response));
    }

    public HttpResult universalTranslator(SimpleHttpResponse response) {
        HttpResult httpResult;
        try {
            String result = StringConstants.EMPTY;
            if (ObjectUtils.isNotEmpty(response)) {
                if (ObjectUtils.isNotEmpty(response.getBody())) {
                    result = new String(response.getBodyBytes(), encoding);
                }
                httpResult = HttpResult.of(response.getCode(), result);
            } else {
                httpResult = HttpResult.of(HttpStatus.SC_INTERNAL_SERVER_ERROR, result);
            }
            return httpResult;
        } catch (Throwable e) {
            httpResult = HttpResult.of(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.toString());
            log.error(LogConstants.error_response_format, httpResult, e);
            return httpResult;
        }
    }

    @Override
    public void failed(Exception ex) {
        HttpResult httpResult = HttpResult.of(HttpStatus.SC_INTERNAL_SERVER_ERROR, ex.toString());
        if (ex instanceof BlockException) {
            httpResult.setCode(HttpExpandStatusEnum.CIRCUIT_BREAK.code);
            log.error(LogConstants.error_response_format, httpResult);
        } else {
            log.error(LogConstants.error_response_format, httpResult, ex.toString(), ex);
        }
        executeCallback(httpResult);
    }

    @Override
    public void cancelled() {
        HttpResult httpResult = HttpResult.of(HttpStatus.SC_INTERNAL_SERVER_ERROR, "cancelled");
        log.error(LogConstants.error_response_format, httpResult);
        callback.doWith(httpResult);
        executeCallback(httpResult);
    }

    public void executeCallback(HttpResult httpResult) {
        if (this.callback != null) {
            this.callback.doWith(httpResult);
        }
    }

    public static FutureCallback of(MethodCallback callback) {
        return new DefaultFutureCallback(callback);
    }

    public static FutureCallback of(MethodCallback callback, String encoding) {
        return new DefaultFutureCallback(callback, encoding);
    }

    @FunctionalInterface
    public interface MethodCallback {
        void doWith(HttpResult httpResult);
    }


}
